#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <netdb.h>
#include <netinet/tcp.h>


#include <xeLOG_internal.h>


#ifndef false
#define false 0
#endif

#ifndef true
#define true 1
#endif

int __is_valid_IP(char *str)
{
	int dot_Count = 0;
	int num_Count = 0;
	int num_Val = 0;

	in_addr_t ipaddr = inet_network(str);

	if (!strcmp(str, "0.0.0.0"))
		return false;

	if (!strcmp(str, "255.255.255.255"))
		return false;

	while ((*str))
	{
		if ((*str != '.'))
		{
			if ((*str) >= '0' && (*str) <= '9')
			{
				++num_Count;
				num_Val = num_Val * 10 + (*str) - '0';
			}
			else
				return false;
		}
		else
		{
			++dot_Count;

			if (num_Count < 1 || num_Count > 3 \
					|| num_Val < 0 || num_Val> 255)
				return false;

			num_Count = 0;
			num_Val = 0;
		}
		++str;
	}

	if (dot_Count != 3)
		return false;

	if(num_Val==0)
		return false;

	if(num_Val==255)
		return false;

	if (IN_MULTICAST(ipaddr))
	{
		return false;
	}

	if(IN_BADCLASS(ipaddr))
	{
		return false;
	}

	/*if((ipaddr&0x7F000000) == 0x7F000000)
	  {
	  return false;
	  }*/

	return true;
}

int __is_valid_netmask(char *str)
{
	int dot_Count = 0;
	int num_Count = 0;
	int num_Val = 0;

	in_addr_t ipaddr = inet_network(str);

	if (!strcmp(str, "0.0.0.0"))
		return false;

	if (!strcmp(str, "255.255.255.255"))
		return false;

	while ((*str))
	{

		if ((*str != '.'))
		{
			if ((*str) >= '0' && (*str) <= '9')
			{
				++num_Count;
				num_Val = num_Val * 10 + (*str) - '0';
			}
			else
				return false;
		}
		else
		{
			++dot_Count;
			if (num_Count < 1 || num_Count > 3 \
					|| num_Val < 0 || num_Val > 255)
				return false;
			num_Count = 0;
			num_Val = 0;
		}
		++str;
	}
	if (dot_Count != 3)
		return false;


	if (ipaddr == 0xFF000000 || ipaddr == 0xFF800000 || ipaddr == 0xFFc00000
			|| ipaddr == 0xFFe00000 || ipaddr == 0xFFF00000 || ipaddr
			== 0xFFF80000 || ipaddr == 0xFFFc0000 || ipaddr == 0xFFFe0000
			|| ipaddr == 0xFFFF0000 || ipaddr == 0xFFFF8000 || ipaddr
			== 0xFFFFc000 || ipaddr == 0xFFFFe000 || ipaddr == 0xFFFFF000
			|| ipaddr == 0xFFFFF800 || ipaddr == 0xFFFFFc00 || ipaddr
			== 0xFFFFFe00 || ipaddr == 0xFFFFFF00 || ipaddr == 0xFFFFFF80
			|| ipaddr == 0xFFFFFFc0 || ipaddr == 0xFFFFFFe0 || ipaddr
			== 0xFFFFFFF0 || ipaddr == 0xFFFFFFF8 || ipaddr == 0xFFFFFFFc)
	{
		return true;
	}

	return false;
}

int __is_valid_gateway(char *str)
{
	return __is_valid_IP(str);
}

in_addr_t get_eth_IP_addr(char *eth_name)
{
	int sockfd;
	struct sockaddr_in addr;
	struct ifreq ifr;

	sockfd=socket(PF_INET,SOCK_DGRAM,0);
	//STD_print("%s socketfd: %d", __FUNCTION__, sockfd);
	if ( sockfd<0 )
		return INADDR_ANY;

	memset(&ifr, 0,  sizeof(struct ifreq));
	strcpy(ifr.ifr_name,eth_name);
	//ifr.ifr_name[4]='\0';
	if ( ioctl(sockfd,SIOCGIFADDR,&ifr)<0 ) {
		close(sockfd);
		return INADDR_ANY;
	}
	memcpy(&addr,&ifr.ifr_addr,sizeof(addr));
	close(sockfd);
	return addr.sin_addr.s_addr;
}

char *_get_eth_IP_addr(char *eth_name) 
{
	int sockfd;
	struct sockaddr_in addr;
	struct ifreq ifr;
	sockfd=socket(PF_INET,SOCK_DGRAM,0);
	//STD_print("%s socketfd: %d", __FUNCTION__, sockfd);
	if (sockfd<0){
		return "127.0.0.1";
	}
	memset(&ifr, 0, sizeof(struct ifreq));
	strcpy(ifr.ifr_name, eth_name);
	//ifr.ifr_name[4]='\0';
	if (ioctl(sockfd, SIOCGIFADDR, &ifr)<0) {
		close(sockfd);
		return "127.0.0.1";
	}
	memcpy(&addr, &ifr.ifr_addr, sizeof(addr));
	close(sockfd);
	return inet_ntoa(addr.sin_addr);
}

int _TCP_keepalive_on(int sockfd)
{
	/**
	 * Aug-20-2008 by dengy<dengy@hisome.com>
	 * add TCP keepalive;
	 **/
	int keepAlive = 1;
	int keepIdle = 6;
	int keepInterval = 1;
	int keepCount = 3;

	/**
	 * SO_KEEPALIVE
	 *        Enable  sending of keep-alive messages on connection-oriented sockets. Expects a integer boolean
	 *        flag.
	 * 
	 **/
	setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE, (void*)&keepAlive,sizeof(keepAlive));
	/**
	 * TCP_KEEPIDLE
	 *        The time (in seconds) the connection needs to remain idle before TCP  starts  sending  keepalive
	 *        probes,  if  the socket option SO_KEEPALIVE has been set on this socket.  This option should not
	 *        be used in code intended to be portable.
	 * 
	 **/
	setsockopt(sockfd,SOL_TCP, TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle));
	/**
	 * TCP_KEEPINTVL
	 *       The time (in seconds) between individual keepalive probes.  This option should not  be  used  in
	 *       code intended to be portable.
	 **/
	setsockopt(sockfd,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval));
	/**
	 * TCP_KEEPCNT
	 *	The  maximum  number  of  keepalive probes TCP should send before dropping the connection.  This
	 *	 option should not be used in code intended to be portable.
	 * 
	 **/
	setsockopt(sockfd,SOL_TCP, TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount));

	return 0;
}


int __TCP_connect_with_timeout(int sockfd,struct sockaddr_in *daddr,struct timeval *tout)
{
	int ret,on=1;
	fd_set wfds={};

	ret = ioctl(sockfd, FIONBIO, &on);
	if(ret == -1){
		xeLOG_ERR("ioctl(FIONBIO) fail, %s",strerror(errno));
		return -1;
	}

	ret = connect(sockfd,(struct sockaddr*)daddr,sizeof(*daddr));
	if(ret==0)
	{
		return 0;/*TCP connection established*/
	}

	if(errno != EINPROGRESS)
	{
		return -1;
	}

	FD_ZERO(&wfds);
	FD_SET(sockfd,&wfds);

	ret = select(sockfd+1,NULL,&wfds,NULL,tout);
	if(FD_ISSET(sockfd,&wfds))
	{
		int optval=-1;
		int optval_len=sizeof(optval);
		if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (int*)&optval,(socklen_t*)&optval_len) !=0)
		{
			return -1;
		}

		if(optval == 0)
		{
			//connected to server
			return 0;
		}

		errno = ECONNREFUSED;
		return -1;
	}
	else
	{
		errno = ETIMEDOUT;
		return -1;
	}
}
#ifndef h_addr
#define h_addr h_addr_list[0]
#endif
in_addr_t z_gethostbyname(const char *name)
{
	struct hostent *ent;
	in_addr_t iaddr = INADDR_ANY;

	if (NULL == name)
	{
		return iaddr;
	}

	ent = gethostbyname(name);
	if (ent == NULL)
	{
		xeLOG_ERR("failed get host by name[%s]\n",name);
		return iaddr;
	}
	memcpy(&iaddr, ent->h_addr, 4);
	return iaddr;
}
